package net.midnighttd.engine.map;

import net.midnighttd.engine.ResourceManager;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.BufferedImage;
import java.awt.*;
import java.util.LinkedList;

/**
 * Author: Adam Scarr
 * Date: 18/04/2009
 * Time: 12:56:18 PM
 */
public class TileBlock {
    private BufferedImage[][] tileset;
    private Tile tile;
    private static Point[] tileLocations = null;

    public TileBlock(String filename, int tilewidth, int tileheight, Tile tile) {
        //System.out.println("Created new tileblock from " + filename + " (" + tilewidth + 'x' + tileheight + ") called " + tile.getName());
        this.tile = tile;
        load_image(filename, tilewidth, tileheight);

        initTileLocations();
    }

    /**
     * Maps directional flags to a point in the tileset.
     *
     * 0 = must not exist
     * 1 = must exist
     * 2 = may exist (all combinations will be added)
     *
     * @param n,ne,e,se,s,sw,w,nw Flags denoting if there is an adjoining tile in that direction.
     * @param dest The location on the tileset we should get the image from.
     */
    @SuppressWarnings({"JavaDoc"})
    private void mapLocationsTo(int n, int ne, int e, int se, int s, int sw, int w, int nw, Point dest) {

        LinkedList<Integer> combinations = new LinkedList<Integer>();

        int baseSignature = 0;
        if(n == 1)  baseSignature += TileSignature.N;
        if(ne == 1) baseSignature += TileSignature.NE;
        if(e == 1)  baseSignature += TileSignature.E;
        if(se == 1) baseSignature += TileSignature.SE;
        if(s == 1)  baseSignature += TileSignature.S;
        if(sw == 1) baseSignature += TileSignature.SW;
        if(w == 1)  baseSignature += TileSignature.W;
        if(nw == 1) baseSignature += TileSignature.NW;

        combinations.add(baseSignature);

        if(n == 2)  addCombinations(combinations, TileSignature.N);
        if(ne == 2) addCombinations(combinations, TileSignature.NE);
        if(e == 2)  addCombinations(combinations, TileSignature.E);
        if(se == 2) addCombinations(combinations, TileSignature.SE);
        if(s == 2)  addCombinations(combinations, TileSignature.S);
        if(sw == 2) addCombinations(combinations, TileSignature.SW);
        if(w == 2)  addCombinations(combinations, TileSignature.W);
        if(nw == 2) addCombinations(combinations, TileSignature.NW);

        for(int mask: combinations) {
            tileLocations[mask] = dest;

        }
    }

    private void addCombinations(LinkedList<Integer> combinations, int mask) {
        LinkedList<Integer> c = new LinkedList<Integer>(combinations);
        for(int combination: c) {
            combinations.add(combination + mask);
        }
    }

    private synchronized void initTileLocations() {
        if(tileLocations != null) return;
        
        tileLocations = new Point[256];
        mapLocationsTo(1,1,1,1,1,1,1,1, new Point(0,0));        // Fully connected
        mapLocationsTo(1,0,1,0,1,0,1,0, new Point(1,1));        // Cross
        mapLocationsTo(0,2,1,2,0,2,1,2, new Point(2,1));        // Left to right bar
        mapLocationsTo(1,2,0,2,1,2,0,2, new Point(1,2));        // Right to left bar
        mapLocationsTo(1,2,0,0,0,0,0,2, new Point(1,4));        // Dead end starting north
        mapLocationsTo(0,2,1,2,0,0,0,0, new Point(0,1));        // Dead end starting east
        mapLocationsTo(0,0,0,2,1,2,0,0, new Point(1,0));        // Dead end starting south
        mapLocationsTo(0,0,0,0,0,2,1,2, new Point(8,1));        // Dead end starting west
        mapLocationsTo(0,2,1,1,1,1,1,2, new Point(4,2));        // Wall facing north.
        mapLocationsTo(1,2,0,2,1,1,1,1, new Point(5,3));        // Wall facing east.
        mapLocationsTo(1,1,1,2,0,2,1,1, new Point(4,4));        // Wall facing down.
        mapLocationsTo(1,1,1,1,1,2,0,2, new Point(3,3));        // Wall facing west.
        mapLocationsTo(0,2,1,0,1,0,1,2, new Point(6,1));        // T facing north.
        mapLocationsTo(1,2,0,2,1,0,1,0, new Point(1,3));        // T facing east.
        mapLocationsTo(1,0,1,2,0,2,1,0, new Point(4,1));        // T facing south.
        mapLocationsTo(1,0,1,0,1,2,0,2, new Point(1,2));        // T facing west.
        mapLocationsTo(1,1,1,0,1,0,1,1, new Point(9,4));        // Wall T facing north
        mapLocationsTo(1,1,1,1,1,0,1,0, new Point(7,5));        // Wall T facing east.
        mapLocationsTo(1,0,1,1,1,1,1,0, new Point(9,3));        // Wall T facing south.
        mapLocationsTo(1,0,1,0,1,1,1,1, new Point(8,6));        // Wall T facing west.
        mapLocationsTo(1,0,1,1,1,1,1,1, new Point(8,3));        // Concave Corner facing ne.
        mapLocationsTo(1,1,1,0,1,1,1,1, new Point(8,4));        // Concave Corner facing se.
        mapLocationsTo(1,1,1,1,1,0,1,1, new Point(7,4));        // Concave Corner facing sw.
        mapLocationsTo(1,1,1,1,1,1,1,0, new Point(7,3));        // Concave Corner facing nw.
        mapLocationsTo(0,2,0,2,1,1,1,2, new Point(5,2));        // Convex corner facing ne.
        mapLocationsTo(1,2,0,2,0,2,1,1, new Point(5,4));        // Convex corner facing se.
        mapLocationsTo(1,1,1,2,0,2,0,2, new Point(3,4));        // Convex corner facing sw
        mapLocationsTo(0,2,1,1,1,2,0,2, new Point(3,2));        // Convex corner facing nw.
        mapLocationsTo(0,2,0,2,1,0,1,2, new Point(5,5));        // elbow facing ne.
        mapLocationsTo(1,2,0,2,0,2,1,0, new Point(5,6));        // elbow facing se.
        mapLocationsTo(1,0,1,2,0,2,0,2, new Point(4,6));        // elbow facing sw
        mapLocationsTo(0,2,1,0,1,2,0,2, new Point(4,5));        // elbow facing nw.
        mapLocationsTo(0,2,0,2,0,2,0,2, new Point(2,0));        // Unconnected spot.
      
    }

    public BufferedImage getTile(TileSignature sig) {
        if(sig.getTile() != tile) return null;

        if(sig.getTile() == tile) {
            Point tile = tileLocations[sig.getSignature()];
            if(tile == null) return null;
            return tileset[tile.y][tile.x];

        } else {
            // There are a few blocks we have that the tile tileblock wont.

            // all but one diagonal connected.
            if(sig.signatureHas(false, false, true, true, true, true, true, false)) return tileset[6][7];
        }

        return null;
    }

    public void load_image(String filename, int tilewidth, int tileheight) {
        BufferedImage raw;
        try {
             raw = ImageIO.read(ResourceManager.get(filename));
        } catch (IOException e) {
            throw new Error("Tileset image loader failed for filename '" + filename + "'. Does it exist?");
        }
        int width = raw.getWidth() / tilewidth;
        int height = raw.getHeight() / tileheight;

        tileset = new BufferedImage[height][width];
        for(int y = 0; y < height; y++) {
            for(int x = 0; x < width; x++) {
                tileset[y][x] = raw.getSubimage(x * tilewidth, y * tileheight, tilewidth, tileheight);
            }
        }
    }

    public BufferedImage getTile(int x, int y) {
        return tileset[y][x];
    }

}
